package com.tengju.support.infrastructure.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.stream.Stream;

@Aspect
@Component
@Slf4j
public class TimeInterceptor {


    @Pointcut("@annotation(com.tengju.support.infrastructure.aop.MonitorTime)")
    private void monitorPointcut() {
    }

    /**
     * 统计方法执行耗时Around环绕通知
     *
     * @param pjp
     * @return
     */
    @Around(value = "monitorPointcut()")
    public Object timeAround(ProceedingJoinPoint pjp) {
        Object obj = null;
        Object[] args = pjp.getArgs();
        long startTime = System.currentTimeMillis();

        String methodName = pjp.getSignature().getName();
        String className = pjp.getTarget().getClass().getName();
        String methodArgs = "";
        if (args.length > 0) {
            StringBuilder sb = new StringBuilder();
            Stream.of(args).forEach(k -> sb.append(k).append(" "));
            sb.deleteCharAt(sb.length() - 1);
            methodArgs = sb.toString();
        }

        try {
            if (args.length > 0) {
                obj = pjp.proceed(args);
            } else {
                obj = pjp.proceed();
            }
        } catch (Exception e) {
            log.error("调用异常,className:{},methodName:{},methodArgs:{},msg:{}", className, methodName, methodArgs, e.getMessage());
        } catch (Throwable e) {
            log.error("调用异常,className:{},methodName:{},methodArgs:{},msg:{}", className, methodName, methodArgs, e.getMessage());
        }

        long endTime = System.currentTimeMillis();

        // 打印耗时的信息
        this.printExecTime(className, methodName, startTime, endTime);

        return obj;
    }

    /**
     * 打印方法执行耗时的信息，如果超过了一定的时间 (3秒)，才打印
     *
     * @param methodName
     * @param startTime
     * @param endTime
     */
    private void printExecTime(String className, String methodName, long startTime, long endTime) {
        long diffTime = endTime - startTime;
        log.info("{}.{}执行耗时{} ms", className, methodName, diffTime);
        if (diffTime > Duration.ofMillis(500).toMillis()) {
            log.warn("{}.{}执行耗时告警{} ms", className, methodName, diffTime);
        }
    }
}
